參考 MDN 對於 window.matchMedia
的說明,可以知道這個方法會根據傳入的 media query 字串回傳對應的查詢結果:
The Window interface's matchMedia() method returns a new MediaQueryList object that can then be used to determine if the document matches the media query string, as well as to monitor the document to detect when it matches (or stops matching) that media query.
把查詢字串 (prefers-color-scheme: light)
傳入 window.matchMedia
後,結果可參考以下內容:
const queryResult = window.matchMedia('(prefers-color-scheme: light)');
console.info(queryResult);
// queryResult 內容如下:
{
media: '(prefers-color-scheme: light)',
matches: false,
onchange: null
}
可以得知現在的系統顏色模式不是淺色模式。
而為了讓使用上更簡單,把以上邏輯打包成 custom hook 來使用,內容如下:
import { useCallback, useEffect, useState } from 'react';
type Scheme = 'light' | 'dark';
type PrefersColorScheme = `(prefers-color-scheme: ${Scheme})`;
const SCHEME_QUERY: PrefersColorScheme = '(prefers-color-scheme: light)';
export default function useSystemColorScheme(): Scheme {
/* States */
const [scheme, setScheme] = useState<Scheme>(
window.matchMedia(SCHEME_QUERY).matches ? 'light' : 'dark'
);
/* Functions */
const updateScheme = useCallback((e: MediaQueryListEvent): void => {
if (e.matches) {
setScheme('light');
} else {
setScheme('dark');
}
}, []);
/* Hooks */
useEffect(() => {
const matchMedia = window.matchMedia(SCHEME_QUERY);
matchMedia.addEventListener('change', updateScheme);
return () => {
matchMedia.removeEventListener('change', updateScheme);
};
}, [updateScheme]);
/* Main */
return scheme;
}
追加了 useEffect 讓 hook 能在系統的顏色模式改變時,回傳更新後的結果。
另一種寫法是直接讓 hook 回傳「系統現在的顏色模式是否為淺(深)色」的布林值,原始碼調整如下:
import { useCallback, useEffect, useState } from 'react';
type Scheme = 'light' | 'dark';
type PrefersColorScheme = `(prefers-color-scheme: ${Scheme})`;
const SCHEME_QUERY: PrefersColorScheme = '(prefers-color-scheme: light)';
function useSystemIsLightMode(): boolean {
/* States */
const [isLightMode, setIsLightMode] = useState<boolean>(
window.matchMedia(SCHEME_QUERY).matches
);
/* Functions */
const updateScheme = useCallback((e: MediaQueryListEvent): void => {
setIsLightMode(e.matches);
}, []);
/* Hooks */
useEffect(() => {
const matchMedia = window.matchMedia(SCHEME_QUERY);
matchMedia.addEventListener('change', updateScheme);
return () => {
matchMedia.removeEventListener('change', updateScheme);
};
}, [updateScheme]);
/* Main */
return isLightMode;
}
要改為預設判定「是否為深色模式」的話就把 SCHEME_QUERY
的內容改為 '(prefers-color-scheme: dark)'
即可。
邏輯本身簡單,搭配昨天介紹的 StyleProvider
元件就能做出「根據使用者的系統顏色模式,來預設要載入深色或是淺色的樣式」?